package se.gala.gw;

import java.util.HashMap;

import se.gala.api.APICommand;
import se.gala.games.quetzalcoatl.ClientCommandRequestor;
import se.gala.utils.GGObject;

public class GWClient extends GGObject {
	private static GCIClientSocket socket;
	private static HashMap<String, ClientCommandRequestor> pendingRequests;
	
	public static int MS_REQUEST_TIMEOUT = 10000;
	public static int MS_SLEEP_BETWEEN_CHECKS = 50;
	
	public static void initClientSocket() {
		initClientSocket(false);
	}
	
	private static void initClientSocket(boolean useStdIn) {
		Thread clientThread = new Thread() {
			public void run() {
				pendingRequests = new HashMap<String, ClientCommandRequestor>(10000);
				log("Initiating Client socket.", LogLevel.Debug);
				if (useStdIn) {
					log("Client commands are expected through Std-IN.");
				}
				socket = new GCIClientSocket(GWServer.HOST, GWServer.PORT, useStdIn);
			}			
		};
		clientThread.start();
	}
		
	/**
	 * Sends an initiated by client request command through the socket.
	 * 
	 * @param command
	 *            The APICommand that needs to be sent.
	 * @param demandResponse
	 *            Set this flag to <code>true</code> to block the thread, until
	 *            a response is received.
	 * @param requestor The object sending the request.
	 */
	public static void sendClientRequest(APICommand command, boolean demandResponse, ClientCommandRequestor requestor) {
		final String fingerPrint = command.getFingerPrint(); 
		registerClientRequest(command, requestor);
		if (demandResponse) {
			socket.sendRequest(command.tokenize());
			try {
				final long startTime = System.currentTimeMillis();
				while (responseReceived(fingerPrint) == false) {
					if (System.currentTimeMillis() - startTime > MS_REQUEST_TIMEOUT) {
						throw new RequestTimedOutException();
					}
					Thread.sleep(MS_SLEEP_BETWEEN_CHECKS);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			socket.sendRequest(command.tokenize());
		}
	}
	
	/**
	 * Unregisters a pending request (to be invoked after its completion).
	 * @param fingerPrint The request identifier.
	 */
	public synchronized static void setResponseHandled(String fingerPrint) {
		pendingRequests.remove(fingerPrint);
	}
	
	private synchronized static void registerClientRequest(APICommand c, ClientCommandRequestor requestor) {
		pendingRequests.put(c.getFingerPrint(), requestor);
	}
	
	private static boolean responseReceived(String fingerPrint) {
		/* if the response is received already, the pendingRequests would be empty */
		return pendingRequests.get(fingerPrint) == null;
	}
	
	protected synchronized static void serverResponseReceived(APICommand c) {
		try {
			pendingRequests.get(c.getFingerPrint()).handleResponseCommand(c);
		} catch (NullPointerException e) {
			// TODO: Handle Server-initiated requests.
			e.printStackTrace();
		} catch (ClassCastException e) {
			// TODO: Handle multiple responses on the same requests
			e.printStackTrace();
		}
		
		setResponseHandled(c.getFingerPrint());
	}
	
//	public static void main(String[] args) {
//		initClientSocket(true);
//	}
}
